<?php 
class Logger
{
	const OFF = 2147483647;
	const FATAL = 50000;
	const ERROR = 40000;
	const WARN = 30000;
	const INFO = 20000;
	const DEBUG = 10000;
	const ALL = -2147483647;
	
	private $logLevel;
	public static $DEFAULT_LOG_LEVEL = Logger::DEBUG;
	public static $fileType = '.log';
	public static $enableFirePHP = false;
	/**
	 * [ 'cate' => [logfile, loglevel, enableFirePHP] ]
	 * @var array
	 */
	public static $loggers = array();
	private $name;
	private $subname;
	private static $_fp = null;
	private static $_fps = array();
	private $fp = null;
	private $firephp = null;
	private $DAY = null; 

	public static function getLogger($name,$path,$setday=true)
	{
		return new Logger($name,$path,$setday);
	}
	
	public static function closeAll()
	{
		if(self::$_fp)
		{
			fclose(self::$_fp);
			self::$_fp = null;
		}
		foreach(self::$_fps as $fp)
		{
			fclose($fp);
		}
		self::$_fps = array();
	}
	
	public function __destruct(){}
	
	private function getFileHandle()
	{
		$DAY = date('Ymd');
		if($this->fp && is_resource($this->fp))
		{
			if($DAY == $this->DAY)
			{
				return $this->fp;
			}
			else
			{
				$this->DAY = $DAY;
				fclose($this->fp);
				$this->fp = null;
			}
		}
		$key = $this->name.$this->subname;
		$file = self::$loggers[$key][0];
		if(strpos($file, '{0}')) $file = str_replace('{0}', $this->name, $file);
		if(strpos($file, '{1}')) $file = str_replace('{1}', $this->subname, $file);
		if(strpos($file, '{DAY}')) $file = str_replace('{DAY}', $DAY, $file);
		if(strpos($file, '{MONTH}')) $file = str_replace('{MONTH}', substr($DAY, 0, 6), $file);
		if(!is_dir(dirname($file))) mkdir(dirname($file));
		self::$_fps[$key] = fopen($file, 'ab');
		$this->fp = self::$_fps[$key];
		return $this->fp;
	}
	
	private function __construct($name,$path,$setday)
	{
		if(strpos($name, ':') === false)
		{
			$this->name = 'log';
			$this->subname = $name;
		} 
		else
		{
			$name = explode(':', $name, 2);
			$this->name = $name[0];
			$this->subname = $name[1];
		}
		$day = $setday ? '_{DAY}' : '';
		$key = $this->name.$this->subname;
		if(!isset(self::$loggers[$key]))
		{
			self::$loggers[$key][] = $path . '/' .$this->name.'_'.$this->subname.$day.self::$fileType;
		}
		if(isset(self::$loggers[$key][1]))
		{
			$this->logLevel = self::$loggers[$key][1];
		}
		else
		{
			$this->logLevel = self::$DEFAULT_LOG_LEVEL;
		}
		if(isset(self::$loggers[$key][2]))
		{
			if(self::$loggers[$key][2])
			{
				$this->firephp = FirePHP::getInstance(true);
			}
		}
		else
		{
			if(self::$enableFirePHP)
			{
				$this->firephp = FirePHP::getInstance(true);
			}
		}
		$this->DAY = date('Ymd');
	}
	
	private function log($message, $level, $tag)
	{
		if($level >= $this->logLevel)
		{
			$now = microtime(true);
			$trace = debug_backtrace();
			$fp = $this->getFileHandle();
			fprintf($fp, "%s,%06d [%s:%04d][%s] %s - %s\n", 
				date('m/d/y H:i:s', intval($now)), 
				1000000*($now-intval($now)), 
				basename($trace[1]['file']),
				$trace[1]['line'], 
				$tag, 
				$this->subname, 
				is_string($message)?$message:var_export($message, true));
			if($this->firephp)
			{
				static $firePHPLevel = array(
					self::DEBUG => FirePHP::LOG,
					self::INFO => FirePHP::INFO,
					self::WARN => FirePHP::WARN,
					self::ERROR => FirePHP::ERROR,
					self::FATAL => FirePHP::ERROR,
				);
				$this->firephp->fb(sprintf("%s,%06d [%s:%04d][%s] %s\n", 
					date('m/d/y H:i:s', intval($now)), 
					1000000*($now-intval($now)), 
					basename($trace[1]['file']),
					$trace[1]['line'], 
					$tag, 
					is_string($message)?$message:':'), $this->subname, $firePHPLevel[$level]);
				if(!is_string($message))
				{
					$this->firephp->fb($message, $this->subname, $firePHPLevel[$level]);
				}
			}
		}
	}

	public function debug($message)
	{
		$this->log($message, self::DEBUG, 'DEBUG');
	}

	public function info($message)
	{
		$this->log($message, self::INFO, 'INFO');
	}

	public function warn($message)
	{
		$this->log($message, self::WARN, 'WARN');
	}
	
	public function error($message)
	{
		$this->log($message, self::ERROR, 'ERROR');
	}
	
	public function fatal($message)
	{
		$this->log($message, self::FATAL, 'FATAL');
	}
	
	public function trace($message)
	{
		$tracks = debug_backtrace();
		foreach ($tracks as $track)
		{
			$this->log($message . " - {$track['function']} ({$track['file']}:{$track['line']})", self::FATAL, 'TRACE');
		}
	}
}

